home *** CD-ROM | disk | FTP | other *** search
/ Windows Expert / Windows Expert.iso / windownt / tusrc.zip / SRC / TAC.C < prev    next >
C/C++ Source or Header  |  1993-10-02  |  18KB  |  656 lines

  1. /* tac - concatenate and print files in reverse
  2.    Copyright (C) 1988, 1989, 1990, 1991 Free Software Foundation, Inc.
  3.  
  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 2, or (at your option)
  7.    any later version.
  8.  
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.    GNU General Public License for more details.
  13.  
  14.    You should have received a copy of the GNU General Public License
  15.    along with this program; if not, write to the Free Software
  16.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  17.  
  18. /* Written by Jay Lepreau (lepreau@cs.utah.edu).
  19.    GNU enhancements by David MacKenzie (djm@gnu.ai.mit.edu). */
  20.  
  21. /* Copy each FILE, or the standard input if none are given or when a
  22.    FILE name of "-" is encountered, to the standard output with the
  23.    order of the records reversed.  The records are separated by
  24.    instances of a string, or a newline if none is given.  By default, the
  25.    separator string is attached to the end of the record that it
  26.    follows in the file.
  27.  
  28.    Options:
  29.    -b, --before            The separator is attached to the beginning
  30.                 of the record that it precedes in the file.
  31.    -r, --regex            The separator is a regular expression.
  32.    -s, --separator=separator    Use SEPARATOR as the record separator.
  33.  
  34.    To reverse a file byte by byte, use (in bash, ksh, or sh):
  35. tac -r -s '.\|
  36. ' file */
  37.  
  38. #include <stdio.h>
  39. #include <io.h>
  40. #include "../lib/getopt.h"
  41. #include <sys/types.h>
  42. #include <signal.h>
  43. #include "../lib/regex.h"
  44. #include "system.h"
  45. #include "version.h"
  46.  
  47. #define SIGHUP      SIGTERM
  48. #define SIGPIPE     99
  49. #define SIGQUIT     SIGTERM
  50.  
  51.  
  52. #ifndef STDC_HEADERS
  53. char *malloc ();
  54. char *realloc ();
  55. #endif
  56.  
  57. /* The number of bytes per atomic read. */
  58. #define INITIAL_READSIZE 8192
  59.  
  60. /* The number of bytes per atomic write. */
  61. #define WRITESIZE 8192
  62.  
  63. char *mktemp ();
  64.  
  65. static RETSIGTYPE cleanup ();
  66. static int tac ();
  67. static int tac_file ();
  68. static int tac_stdin ();
  69. static char *xmalloc ();
  70. static char *xrealloc ();
  71. static void output ();
  72. static void save_stdin ();
  73. static void xwrite ();
  74.  
  75. void error ();
  76.  
  77. /* The name this program was run with. */
  78. char *program_name;
  79.  
  80. /* The string that separates the records of the file. */
  81. static char *separator;
  82.  
  83. /* If nonzero, print `separator' along with the record preceding it
  84.    in the file; otherwise with the record following it. */
  85. static int separator_ends_record;
  86.  
  87. /* 0 if `separator' is to be matched as a regular expression;
  88.    otherwise, the length of `separator', used as a sentinel to
  89.    stop the search. */
  90. static int sentinel_length;
  91.  
  92. /* The length of a match with `separator'.  If `sentinel_length' is 0,
  93.    `match_length' is computed every time a match succeeds;
  94.    otherwise, it is simply the length of `separator'. */
  95. static int match_length;
  96.  
  97. /* The input buffer. */
  98. static char *buffer;
  99.  
  100. /* The number of bytes to read at once into `buffer'. */
  101. static unsigned read_size;
  102.  
  103. /* The size of `buffer'.  This is read_size * 2 + sentinel_length + 2.
  104.    The extra 2 bytes allow `past_end' to have a value beyond the
  105.    end of `buffer' and `match_start' to run off the front of `buffer'. */
  106. static unsigned buffer_size;
  107.  
  108. /* The compiled regular expression representing `separator'. */
  109. static struct re_pattern_buffer compiled_separator;
  110.  
  111. /* If non-zero, display usage information and exit.  */
  112. static int flag_help;
  113.  
  114. /* If non-zero, print the version on standard error.  */
  115. static int flag_version;
  116.  
  117. static struct option const longopts[] =
  118. {
  119.   {"before", no_argument, &separator_ends_record, 0},
  120.   {"regex", no_argument, &sentinel_length, 0},
  121.   {"separator", required_argument, NULL, 's'},
  122.   {"help", no_argument, &flag_help, 1},
  123.   {"version", no_argument, &flag_version, 1},
  124.   {NULL, 0, NULL, 0}
  125. };
  126.  
  127. static void
  128. usage ()
  129. {
  130.   fprintf (stderr, "\
  131. Usage: %s [-br] [-s separator] [--before] [--regex] [--separator=separator]\n\
  132.       [--help] [--version] [file...]\n",
  133.        program_name);
  134.   exit (1);
  135. }
  136.  
  137. void
  138. main (argc, argv)
  139.      int argc;
  140.      char **argv;
  141. {
  142.   const char *error_message;    /* Return value from re_compile_pattern. */
  143.   int optc, errors;
  144.   int have_read_stdin = 0;
  145.  
  146.   program_name = argv[0];
  147.   errors = 0;
  148.   separator = "\n";
  149.   sentinel_length = 1;
  150.   separator_ends_record = 1;
  151.  
  152.   while ((optc = getopt_long (argc, argv, "brs:", longopts, (int *) 0))
  153.      != EOF)
  154.     {
  155.       switch (optc)
  156.     {
  157.     case 0:
  158.       break;
  159.     case 'b':
  160.       separator_ends_record = 0;
  161.       break;
  162.     case 'r':
  163.       sentinel_length = 0;
  164.       break;
  165.     case 's':
  166.       separator = optarg;
  167.       if (*separator == 0)
  168.         error (1, 0, "separator cannot be empty");
  169.       break;
  170.     default:
  171.       usage ();
  172.     }
  173.     }
  174.  
  175.   if (flag_version)
  176.     {
  177.       fprintf (stderr, "%s\n", version_string);
  178.       exit (0);
  179.     }
  180.  
  181.   if (flag_help)
  182.     usage ();
  183.  
  184.   if (sentinel_length == 0)
  185.     {
  186.       compiled_separator.allocated = 100;
  187.       compiled_separator.buffer = (unsigned char *)
  188.     xmalloc (compiled_separator.allocated);
  189.       compiled_separator.fastmap = xmalloc (256);
  190.       compiled_separator.translate = 0;
  191.       error_message = re_compile_pattern (separator, strlen (separator),
  192.                       &compiled_separator);
  193.       if (error_message)
  194.     error (1, 0, "%s", error_message);
  195.     }
  196.   else
  197.     match_length = sentinel_length = strlen (separator);
  198.  
  199.   read_size = INITIAL_READSIZE;
  200.   /* A precaution that will probably never be needed. */
  201.   while ((unsigned)(sentinel_length) * 2 >= read_size)
  202.     read_size *= 2;
  203.   buffer_size = read_size * 2 + sentinel_length + 2;
  204.   buffer = xmalloc (buffer_size);
  205.   if (sentinel_length)
  206.     {
  207.       strcpy (buffer, separator);
  208.       buffer += sentinel_length;
  209.     }
  210.   else
  211.     ++buffer;
  212.  
  213.   if (optind == argc)
  214.     {
  215.       have_read_stdin = 1;
  216.       errors = tac_stdin ();
  217.     }
  218.   else
  219.     for (; optind < argc; ++optind)
  220.       {
  221.     if (strcmp (argv[optind], "-") == 0)
  222.       {
  223.         have_read_stdin = 1;
  224.         errors |= tac_stdin ();
  225.       }
  226.     else
  227.       errors |= tac_file (argv[optind]);
  228.       }
  229.  
  230.   /* Flush the output buffer. */
  231.   output ((char *) NULL, (char *) NULL);
  232.  
  233.   if (have_read_stdin && close (0) < 0)
  234.     error (1, errno, "-");
  235.   if (close (1) < 0)
  236.     error (1, errno, "write error");
  237.   exit (errors);
  238. }
  239.  
  240. /* The name of a temporary file containing a copy of pipe input. */
  241. char *tempfile;
  242.  
  243. /* Print the standard input in reverse, saving it to temporary
  244.    file `tempfile' first if it is a pipe.
  245.    Return 0 if ok, 1 if an error occurs. */
  246.  
  247. static int
  248. tac_stdin ()
  249. {
  250.   /* Previous values of signal handlers. */
  251.   RETSIGTYPE (*sigint) (), (*sighup) (), (*sigpipe) (), (*sigterm) ();
  252.   int errors;
  253.   struct stat stats;
  254. #ifdef _POSIX_VERSION
  255.     struct sigaction oldact, newact;
  256. #endif                /* _POSIX_VERSION */
  257.  
  258.   /* No tempfile is needed for "tac < file".
  259.      Use fstat instead of checking for errno == ESPIPE because
  260.      lseek doesn't work on some special files but doesn't return an
  261.      error, either. */
  262.   if (fstat (0, &stats))
  263.     {
  264.       error (0, errno, "standard input");
  265.       return 1;
  266.     }
  267.   if (S_ISREG (stats.st_mode))
  268.     return tac (0, "standard input");
  269.  
  270. #ifdef _POSIX_VERSION
  271.   newact.sa_handler = cleanup;
  272.   sigemptyset (&newact.sa_mask);
  273.   newact.sa_flags = 0;
  274.  
  275.   sigaction (SIGINT, NULL, &oldact);
  276.   sigint = oldact.sa_handler;
  277.   if (sigint != SIG_IGN)
  278.     sigaction (SIGINT, &newact, NULL);
  279.  
  280.   sigaction (SIGHUP, NULL, &oldact);
  281.   sighup = oldact.sa_handler;
  282.   if (sighup != SIG_IGN)
  283.     sigaction (SIGHUP, &newact, NULL);
  284.  
  285.   sigaction (SIGPIPE, NULL, &oldact);
  286.   sigpipe = oldact.sa_handler;
  287.   if (sigpipe != SIG_IGN)
  288.     sigaction (SIGPIPE, &newact, NULL);
  289.  
  290.   sigaction (SIGTERM, NULL, &oldact);
  291.   sigterm = oldact.sa_handler;
  292.   if (sigterm != SIG_IGN)
  293.     sigaction (SIGTERM, &newact, NULL);
  294. #else                /* !_POSIX_VERSION */
  295.   sigint = signal (SIGINT, SIG_IGN);
  296.   if (sigint != SIG_IGN)
  297.     signal (SIGINT, (void *) cleanup);
  298.  
  299.   sighup = signal (SIGHUP, SIG_IGN);
  300.   if (sighup != SIG_IGN)
  301.     signal (SIGHUP, (void *) cleanup);
  302.  
  303.   sigpipe = signal (SIGPIPE, SIG_IGN);
  304.   if (sigpipe != SIG_IGN)
  305.     signal (SIGPIPE, (void *) cleanup);
  306.  
  307.   sigterm = signal (SIGTERM, SIG_IGN);
  308.   if (sigterm != SIG_IGN)
  309.     signal (SIGTERM, (void *) cleanup);
  310. #endif                /* _POSIX_VERSION */
  311.  
  312.   save_stdin ();
  313.  
  314.   errors = tac_file (tempfile);
  315.  
  316.   unlink (tempfile);
  317.  
  318. #ifdef _POSIX_VERSION
  319.   newact.sa_handler = sigint;
  320.   sigaction (SIGINT, &newact, NULL);
  321.   newact.sa_handler = sighup;
  322.   sigaction (SIGHUP, &newact, NULL);
  323.   newact.sa_handler = sigterm;
  324.   sigaction (SIGTERM, &newact, NULL);
  325.   newact.sa_handler = sigpipe;
  326.   sigaction (SIGPIPE, &newact, NULL);
  327. #else                /* !_POSIX_VERSION */
  328.   signal (SIGINT, (void *) sigint);
  329.   signal (SIGHUP, (void *) sighup);
  330.   signal (SIGTERM, (void *) sigterm);
  331.   signal (SIGPIPE, (void *) sigpipe);
  332. #endif                /* _POSIX_VERSION */
  333.  
  334.   return errors;
  335. }
  336.  
  337. /* Make a copy of the standard input in `tempfile'. */
  338.  
  339. static void
  340. save_stdin ()
  341. {
  342.   static char *template = NULL;
  343.   static char *tempdir;
  344.   int fd;
  345.   int bytes_read;
  346.  
  347.   if (template == NULL)
  348.     {
  349.       tempdir = getenv ("TEMP");
  350.       if (tempdir == NULL)
  351.     tempdir = "/tmp";
  352.       template = xmalloc (strlen (tempdir) + 11);
  353.     }
  354.   sprintf (template, "%s/tacXXXXXX", tempdir);
  355.   tempfile = mktemp (template);
  356.  
  357.   fd = creat (tempfile, 0600);
  358.   if (fd == -1)
  359.     {
  360.       error (0, errno, "%s", tempfile);
  361.       cleanup ();
  362.     }
  363.   while ((bytes_read = read (0, buffer, read_size)) > 0)
  364.     if (write (fd, buffer, bytes_read) != bytes_read)
  365.       {
  366.     error (0, errno, "%s", tempfile);
  367.     cleanup ();
  368.       }
  369.   if (close (fd) < 0)
  370.     {
  371.       error (0, errno, "%s", tempfile);
  372.       cleanup ();
  373.     }
  374.   if (bytes_read == -1)
  375.     {
  376.       error (0, errno, "read error");
  377.       cleanup ();
  378.     }
  379. }
  380.  
  381. /* Print FILE in reverse.
  382.    Return 0 if ok, 1 if an error occurs. */
  383.  
  384. static int
  385. tac_file (file)
  386.      char *file;
  387. {
  388.   int fd, errors;
  389.  
  390.   fd = open (file, 0);
  391.   if (fd == -1)
  392.     {
  393.       error (0, errno, "%s", file);
  394.       return 1;
  395.     }
  396.   errors = tac (fd, file);
  397.   if (close (fd) < 0)
  398.     {
  399.       error (0, errno, "%s", file);
  400.       return 1;
  401.     }
  402.   return errors;
  403. }
  404.  
  405. /* Print in reverse the file open on descriptor FD for reading FILE.
  406.    Return 0 if ok, 1 if an error occurs. */
  407.  
  408. static int
  409. tac (fd, file)
  410.      int fd;
  411.      char *file;
  412. {
  413.   /* Pointer to the location in `buffer' where the search for
  414.      the next separator will begin. */
  415.   char *match_start;
  416.   /* Pointer to one past the rightmost character in `buffer' that
  417.      has not been printed yet. */
  418.   char *past_end;
  419.   unsigned saved_record_size;    /* Length of the record growing in `buffer'. */
  420.   off_t file_pos;        /* Offset in the file of the next read. */
  421.   /* Nonzero if `output' has not been called yet for any file.
  422.      Only used when the separator is attached to the preceding record. */
  423.   int first_time = 1;
  424.   char first_char = *separator;    /* Speed optimization, non-regexp. */
  425.   char *separator1 = separator + 1; /* Speed optimization, non-regexp. */
  426.   int match_length1 = match_length - 1; /* Speed optimization, non-regexp. */
  427.   struct re_registers regs;
  428.  
  429.   /* Find the size of the input file. */
  430.   file_pos = lseek (fd, (off_t) 0, SEEK_END);
  431.   if (file_pos < 1)
  432.     return 0;            /* It's an empty file. */
  433.  
  434.   /* Arrange for the first read to lop off enough to leave the rest of the
  435.      file a multiple of `read_size'.  Since `read_size' can change, this may
  436.      not always hold during the program run, but since it usually will, leave
  437.      it here for i/o efficiency (page/sector boundaries and all that).
  438.      Note: the efficiency gain has not been verified. */
  439.   saved_record_size = file_pos % read_size;
  440.   if (saved_record_size == 0)
  441.     saved_record_size = read_size;
  442.   file_pos -= saved_record_size;
  443.   /* `file_pos' now points to the start of the last (probably partial) block
  444.      in the input file. */
  445.  
  446.   lseek (fd, file_pos, SEEK_SET);
  447.   read (fd, buffer, saved_record_size);
  448.  
  449.   match_start = past_end = buffer + saved_record_size;
  450.   /* For non-regexp search, move past impossible positions for a match. */
  451.   if (sentinel_length)
  452.     match_start -= match_length1;
  453.  
  454.   for (;;)
  455.     {
  456.       /* Search backward from `match_start' - 1 to `buffer' for a match
  457.      with `separator'; for speed, use strncmp if `separator' contains no
  458.      metacharacters.
  459.      If the match succeeds, set `match_start' to point to the start of
  460.      the match and `match_length' to the length of the match.
  461.      Otherwise, make `match_start' < `buffer'. */
  462.       if (sentinel_length == 0)
  463.     {
  464.       int i = match_start - buffer;
  465.       int ret;
  466.  
  467.       ret = re_search (&compiled_separator, buffer, i, i - 1, -i, ®s);
  468.       if (ret == -1)
  469.         match_start = buffer - 1;
  470.       else if (ret == -2)
  471.         {
  472.           error (0, 0, "error in regular expression search");
  473.           cleanup ();
  474.         }
  475.       else
  476.         {
  477.           match_start = buffer + regs.start[0];
  478.           match_length = regs.end[0] - regs.start[0];
  479.         }
  480.     }
  481.       else
  482.     {
  483.       /* `match_length' is constant for non-regexp boundaries. */
  484.       while (*--match_start != first_char
  485.          || (match_length1 && strncmp (match_start + 1, separator1,
  486.                            match_length1)))
  487.         /* Do nothing. */ ;
  488.     }
  489.  
  490.       /* Check whether we backed off the front of `buffer' without finding
  491.          a match for `separator'. */
  492.       if (match_start < buffer)
  493.     {
  494.       if (file_pos == 0)
  495.         {
  496.           /* Hit the beginning of the file; print the remaining record. */
  497.           output (buffer, past_end);
  498.           return 0;
  499.         }
  500.  
  501.       saved_record_size = past_end - buffer;
  502.       if (saved_record_size > read_size)
  503.         {
  504.           /* `buffer_size' is about twice `read_size', so since
  505.          we want to read in another `read_size' bytes before
  506.          the data already in `buffer', we need to increase
  507.          `buffer_size'. */
  508.           char *newbuffer;
  509.           int offset = sentinel_length ? sentinel_length : 1;
  510.  
  511.           read_size *= 2;
  512.           buffer_size = read_size * 2 + sentinel_length + 2;
  513.           newbuffer = xrealloc (buffer - offset, buffer_size) + offset;
  514.           /* Adjust the pointers for the new buffer location.  */
  515.           match_start += newbuffer - buffer;
  516.           past_end += newbuffer - buffer;
  517.           buffer = newbuffer;
  518.         }
  519.  
  520.       /* Back up to the start of the next bufferfull of the file.  */
  521.       if (file_pos >= (long) read_size)
  522.         file_pos -= read_size;
  523.       else
  524.         {
  525.           read_size = file_pos;
  526.           file_pos = 0;
  527.         }
  528.       lseek (fd, file_pos, SEEK_SET);
  529.  
  530.       /* Shift the pending record data right to make room for the new. */
  531.       bcopy (buffer, buffer + read_size, saved_record_size);
  532.       past_end = buffer + read_size + saved_record_size;
  533.       /* For non-regexp searches, avoid unneccessary scanning. */
  534.       if (sentinel_length)
  535.         match_start = buffer + read_size;
  536.       else
  537.         match_start = past_end;
  538.  
  539.       if ((unsigned) read (fd, buffer, read_size) != read_size)
  540.         {
  541.           error (0, errno, "%s", file);
  542.           return 1;
  543.         }
  544.     }
  545.       else
  546.     {
  547.       /* Found a match of `separator'. */
  548.       if (separator_ends_record)
  549.         {
  550.           char *match_end = match_start + match_length;
  551.  
  552.           /* If this match of `separator' isn't at the end of the
  553.              file, print the record. */
  554.           if (first_time == 0 || match_end != past_end)
  555.         output (match_end, past_end);
  556.           past_end = match_end;
  557.           first_time = 0;
  558.         }
  559.       else
  560.         {
  561.           output (match_start, past_end);
  562.           past_end = match_start;
  563.         }
  564.       match_start -= match_length - 1;
  565.     }
  566.     }
  567. }
  568.  
  569. /* Print the characters from START to PAST_END - 1.
  570.    If START is NULL, just flush the buffer. */
  571.  
  572. static void
  573. output (start, past_end)
  574.      char *start;
  575.      char *past_end;
  576. {
  577.   static char buffer[WRITESIZE];
  578.   static int bytes_in_buffer = 0;
  579.   int bytes_to_add = past_end - start;
  580.   int bytes_available = WRITESIZE - bytes_in_buffer;
  581.  
  582.   if (start == 0)
  583.     {
  584.       xwrite (1, buffer, bytes_in_buffer);
  585.       bytes_in_buffer = 0;
  586.       return;
  587.     }
  588.   
  589.   /* Write out as many full buffers as possible. */
  590.   while (bytes_to_add >= bytes_available)
  591.     {
  592.       bcopy (start, buffer + bytes_in_buffer, bytes_available);
  593.       bytes_to_add -= bytes_available;
  594.       start += bytes_available;
  595.       xwrite (1, buffer, WRITESIZE);
  596.       bytes_in_buffer = 0;
  597.       bytes_available = WRITESIZE;
  598.     }
  599.  
  600.   bcopy (start, buffer + bytes_in_buffer, bytes_to_add);
  601.   bytes_in_buffer += bytes_to_add;
  602. }
  603.  
  604. static RETSIGTYPE
  605. cleanup ()
  606. {
  607.   unlink (tempfile);
  608.   exit (1);
  609. }
  610.  
  611. static void
  612. xwrite (desc, buffer, size)
  613.      int desc;
  614.      char *buffer;
  615.      int size;
  616. {
  617.   if (write (desc, buffer, size) != size)
  618.     {
  619.       error (0, errno, "write error");
  620.       cleanup ();
  621.     }
  622. }
  623.  
  624. /* Allocate N bytes of memory dynamically, with error checking.  */
  625.  
  626. static char *
  627. xmalloc (n)
  628.      unsigned n;
  629. {
  630.   char *p;
  631.  
  632.   p = malloc (n);
  633.   if (p == 0)
  634.     {
  635.       error (0, 0, "virtual memory exhausted");
  636.       cleanup ();
  637.     }
  638.   return p;
  639. }
  640.  
  641. /* Change the size of memory area P to N bytes, with error checking. */
  642.  
  643. static char *
  644. xrealloc (p, n)
  645.      char *p;
  646.      unsigned n;
  647. {
  648.   p = realloc (p, n);
  649.   if (p == 0)
  650.     {
  651.       error (0, 0, "virtual memory exhausted");
  652.       cleanup ();
  653.     }
  654.   return p;
  655. }
  656.